package com.qingzhu.component.lock.way.zookeeper.generator;

import com.qingzhu.component.lock.annotation.generator.LockGeneratorBean;
import com.qingzhu.component.lock.common.cache.LockCache;
import com.qingzhu.component.lock.common.cache.expire.strategy.CacheOutStrategy;
import com.qingzhu.component.lock.common.config.LockProperties;
import com.qingzhu.component.lock.common.config.enums.ZookeeperEnumConfig;
import com.qingzhu.component.lock.common.constant.AutoConfig;
import com.qingzhu.component.lock.common.constant.locktype.LockTypeConstant;
import com.qingzhu.component.lock.common.entity.LockEntity;
import com.qingzhu.component.lock.common.exception.LockCreatedException;
import com.qingzhu.component.lock.common.generator.LockGenerator;
import com.qingzhu.component.lock.common.pool.ForkLockExecutor;
import com.qingzhu.component.lock.common.util.BaseUtil;
import com.qingzhu.component.lock.way.zookeeper.entity.ZookeeperLockEntity;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.RetryOneTime;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;
import org.springframework.beans.factory.annotation.Value;

import java.util.Map;

/**
 * zookeeper的锁生成器
 * @author xiangjz
 * @version 1.0
 * @date 2021/1/14 17:09
 */
@LockGeneratorBean(value = "zookeeperLockGenerator", type = LockTypeConstant.LOCK_TYPE_ZOOKEEPER)
@Slf4j
public class ZookeeperLockGenerator extends LockGenerator<String> {

    private final CuratorFramework curatorFramework;
    private final String rootLockPath;

    public ZookeeperLockGenerator(CacheOutStrategy cacheOutStrategy,
                                  LockProperties lockProperties,
                                  @Value("${spring.application.name}") String applicationName,
                                  ForkLockExecutor forkLockExecutor) {
        super(cacheOutStrategy, forkLockExecutor);
        Map<String, Object> zookeeperConfig = lockProperties.getZookeeper();
        if(BaseUtil.isEmpty(zookeeperConfig)) {
            throw new IllegalStateException("zookeeperConfig is invalid, please make sure that config [zookeeper] exist below root config [lock]");
        }
        String rootLockPathPrefix = "/lock-" + (BaseUtil.isEmpty(applicationName) ? "" : applicationName + "-");
        this.rootLockPath = rootLockPathPrefix + BaseUtil.retStr(ZookeeperEnumConfig.getOrDefault(zookeeperConfig, ZookeeperEnumConfig.ROOT_PATH));
        String connectionUrl = BaseUtil.retStr(ZookeeperEnumConfig.getOrDefault(zookeeperConfig, ZookeeperEnumConfig.CONNECTION_URL));
        curatorFramework = CuratorFrameworkFactory.builder()
                .connectString(connectionUrl)
                .sessionTimeoutMs(3000)
//                .retryPolicy(new ExponentialBackoffRetry(5000, 5))
                .retryPolicy(new RetryOneTime(1000))
                .build();
        curatorFramework.start();
        // 先创建好锁的根节点
        try {
            // 创建一个带超时时间的永久锁节点，用于当根节点发生变更时，自动过期不需要的节点
            Stat stat = curatorFramework.checkExists().creatingParentsIfNeeded().forPath(rootLockPath);
            // 判断是否已经创建，用于非首次创建时验证，由于首次创建反正都是空节点，所以不考虑首次并发安全
            if(BaseUtil.isEmpty(stat)) {
//                long ttl = Long.parseLong(BaseUtil.retStr(ZookeeperEnumConfig.getOrDefault(zookeeperConfig, ZookeeperEnumConfig.ROOT_PATH_TTL)));
                curatorFramework.create().creatingParentsIfNeeded().
                        withMode(CreateMode.PERSISTENT).forPath(rootLockPath);
            }
        } catch (Exception e) {
            log.error("{} node[{}] of Zookeeper creating failed, cause is {}", AutoConfig.COMPONENT_LOCK_LOG_PREFIX, this.rootLockPath, e);
            throw new LockCreatedException("node["+this.rootLockPath+"] of Zookeeper creating failed");
        }
    }

    @Override
    public LockEntity doGetLock(String key, long expireTime) {
        return new ZookeeperLockEntity(key, expireTime, curatorFramework, rootLockPath + "/" + key);
    }

}
